<?php

namespace App\Services\YinShangPay;

//主扫
class CBPayService
{
    //接主扫，被扫【支付，查询，退款，退款查询】

    const GetQrCode = "https://api-mop.chinaums.com/v1/netpay/bills/get-qrcode";//获取二维码
    const UpQrcode = "https://api-mop.chinaums.com/v1/netpay/bills/update-qrcode";//更新二维码
    const CloseQrcode = "https://api-mop.chinaums.com/v1/netpay/bills/close-qrcode";//关闭二维码
    const Query = "https://api-mop.chinaums.com/v1/netpay/bills/query";//查询
    const Refund = "https://api-mop.chinaums.com/v1/netpay/bills/refund";//退款
    const QueryStaticQrCode = "https://api-mop.chinaums.com/v1/netpay/bills/query-qrcode-info";//查询静态二维码
    const SecureCancel = "https://api-mop.chinaums.com/v1/netpay/secure-cancel";//担保撤销
    const SecureComplete = "https://api-mop.chinaums.com/v1/netpay/secure-complete";//担保完成
    const Preauthed = "https://api-mop.chinaums.com/v1/netpay/bills/preauthed";//预授权完成
    const PreauthCancel = "https://api-mop.chinaums.com/v1/netpay/bills/preauth-cancel";//预授权撤销
    const PreauthedCancel = "https://api-mop.chinaums.com/v1/netpay/bills/preauthed-cancel";//预授权完成撤销
    const SubOrdersConfirm = "https://api-mop.chinaums.com/v1/netpay/sub-orders-confirm";//异步分账确认
    const AccessToken = "https://api-mop.chinaums.com/v2/token/access";

    //token
    public  $appId = "8a81c1bd831e4c960187bcb1aea14a66";
    public  $appKey = "8a81c1bd831e4c960187bcb1aea14a66";
    public  $merchantId = "898341286995736";
    public  $terminalCode = "12571667";

    public function __construct(string $appId, string $appKey, string $merchantId, string $terminalCode)
    {
        $this->appId = $appId;
        $this->appKey = $appKey;
        $this->merchantId = $merchantId;
        $this->terminalCode = $terminalCode;
    }


    public function subOrdersConfirm(array $param): array
    {
        if (!isset($param["requestTimestamp"])) {
            $requestTimestamp = date("Y-m-d H:i:s", time());
        } else {
            $requestTimestamp = $param["requestTimestamp"];
        }

        $data = [
            "requestTimestamp" => $requestTimestamp,//2020-02-26 13:36:00
            "merOrderId" => $param["merOrderId"] ?? "",
            "mid" => $this->merchantId,
            "tid" => $this->terminalCode,
            "platformAmount" => $param["platformAmount"],
        ];
        $body = json_encode($data);
//        $token = self::Token;
//        if (empty($token)) {
//            $token = $this->getAccessToken();
//        }
//        $header=[
//            'Content-Type: application/json',
//            'Authorization:OPEN-ACCESS-TOKEN AccessToken="'.$token.'",AppId="'.self::AppId.'"'
//        ];
        $header = [
            'Content-Type: application/json',
            'Content-Length: ' . strlen($body),
            'Authorization: ' . $this->getSign($body)
        ];

        $res = $this->curl(self::SubOrdersConfirm, 'POST', $body,
            $header);
        return $res;
    }

    public function preauthedCancel(array $param): array
    {
        if (!isset($param["requestTimestamp"])) {
            $requestTimestamp = date("Y-m-d H:i:s", time());
        } else {
            $requestTimestamp = $param["requestTimestamp"];
        }
        $data = [
            "requestTimestamp" => $requestTimestamp,//2020-02-26 13:36:00
            "mid" => $this->merchantId,
            "tid" => $this->terminalCode,
            "instMid" => "QRPAYDEFAULT",
            "billNo" => $param["billNo"],//31942002267733902130410551
            "billDate" => $param['billDate'],//date("Y-m-d", time())//2022-02-26
            "merOrderId" => $param["merOrderId"],
            "authIdRespCd" => $param["authIdRespCd"],
        ];
        $body = json_encode($data);
//        $token = self::Token;
//        if (empty($token)) {
//            $token = $this->getAccessToken();
//        }
//        $header=[
//            'Content-Type: application/json',
//            'Authorization:OPEN-ACCESS-TOKEN AccessToken="'.$token.'",AppId="'.self::AppId.'"'
//        ];
        $header = [
            'Content-Type: application/json',
            'Content-Length: ' . strlen($body),
            'Authorization: ' . $this->getSign($body)
        ];

        $res = $this->curl(self::PreauthedCancel, 'POST', $body,
            $header);
        return $res;
    }

    public function preauthCancel(array $param): array
    {
        if (!isset($param["requestTimestamp"])) {
            $requestTimestamp = date("Y-m-d H:i:s", time());
        } else {
            $requestTimestamp = $param["requestTimestamp"];
        }
        $data = [
            "requestTimestamp" => $requestTimestamp,//2020-02-26 13:36:00
            "mid" => $this->merchantId,
            "tid" => $this->terminalCode,
            "instMid" => "QRPAYDEFAULT",
            "billNo" => $param["billNo"],//31942002267733902130410551
            "billDate" => $param["billDate"],//date("Y-m-d", time()),//2022-02-26
            "merOrderId" => $param["merOrderId"],
            "authIdRespCd" => $param["authIdRespCd"],
        ];
        $body = json_encode($data);
//        $token = self::Token;
//        if (empty($token)) {
//            $token = $this->getAccessToken();
//        }
//        $header=[
//            'Content-Type: application/json',
//            'Authorization:OPEN-ACCESS-TOKEN AccessToken="'.$token.'",AppId="'.self::AppId.'"'
//        ];
        $header = [
            'Content-Type: application/json',
            'Content-Length: ' . strlen($body),
            'Authorization: ' . $this->getSign($body)
        ];

        $res = $this->curl(self::PreauthCancel, 'POST', $body,
            $header);
        return $res;
    }

    public function preauthed(array $param): array
    {
        if (!isset($param["requestTimestamp"])) {
            $requestTimestamp = date("Y-m-d H:i:s", time());
        } else {
            $requestTimestamp = $param["requestTimestamp"];
        }
        $data = [
            "requestTimestamp" => $requestTimestamp,//2020-02-26 13:36:00
            "mid" => $this->merchantId,
            "tid" => $this->terminalCode,
            "instMid" => "QRPAYDEFAULT",
            "billNo" => $param["billNo"],
            "billDate" => $param["billDate"],
            "authedOrderId" => $param["authedOrderId"],
            "authIdRespCd" => $param["authIdRespCd"],
            "totalAmount" => $param["totalAmount"],
        ];
        $body = json_encode($data);
//        $token = self::Token;
//        if (empty($token)) {
//            $token = $this->getAccessToken();
//        }
//        $header=[
//            'Content-Type: application/json',
//            'Authorization:OPEN-ACCESS-TOKEN AccessToken="'.$token.'",AppId="'.self::AppId.'"'
//        ];
        $header = [
            'Content-Type: application/json',
            'Content-Length: ' . strlen($body),
            'Authorization: ' . $this->getSign($body)
        ];

        $res = $this->curl(self::Preauthed, 'POST', $body,
            $header);
        return $res;
    }

    public function secureComplete(array $param): array
    {
        if (!isset($param["requestTimestamp"])) {
            $requestTimestamp = date("Y-m-d H:i:s", time());
        } else {
            $requestTimestamp = $param["requestTimestamp"];
        }
        $data = [
            "requestTimestamp" => $requestTimestamp,//2020-02-26 13:36:00
            "mid" => $this->merchantId,
            "tid" => $this->terminalCode,
            "instMid" => "QRPAYDEFAULT",
            "merOrderId" => $param["merOrderId"],
            "completedAmount" => $param["completedAmount"]
        ];
        $body = json_encode($data);
//        $token = self::Token;
//        if (empty($token)) {
//            $token = $this->getAccessToken();
//        }
//        $header=[
//            'Content-Type: application/json',
//            'Authorization:OPEN-ACCESS-TOKEN AccessToken="'.$token.'",AppId="'.self::AppId.'"'
//        ];
        $header = [
            'Content-Type: application/json',
            'Content-Length: ' . strlen($body),
            'Authorization: ' . $this->getSign($body)
        ];

        $res = $this->curl(self::SecureComplete, 'POST', $body,
            $header);
        return $res;
    }

    public function secureCancel(array $param): array
    {
        if (!isset($param["requestTimestamp"])) {
            $requestTimestamp = date("Y-m-d H:i:s", time());
        } else {
            $requestTimestamp = $param["requestTimestamp"];
        }
        $data = [
            "requestTimestamp" => $requestTimestamp,//2020-02-26 13:36:00
            "mid" => $this->merchantId,
            "tid" => $this->terminalCode,
            "instMid" => "QRPAYDEFAULT",
            "merOrderId" => $param["merOrderId"],
        ];
        $body = json_encode($data);
//        $token = self::Token;
//        if (empty($token)) {
//            $token = $this->getAccessToken();
//        }
//        $header=[
//            'Content-Type: application/json',
//            'Authorization:OPEN-ACCESS-TOKEN AccessToken="'.$token.'",AppId="'.self::AppId.'"'
//        ];
        $header = [
            'Content-Type: application/json',
            'Content-Length: ' . strlen($body),
            'Authorization: ' . $this->getSign($body)
        ];

        $res = $this->curl(self::SecureCancel, 'POST', $body,
            $header);
        return $res;
    }

    public function queryQrcodeInfo(array $param): array
    {
        if (!isset($param["requestTimestamp"])) {
            $requestTimestamp = date("Y-m-d H:i:s", time());
        } else {
            $requestTimestamp = $param["requestTimestamp"];
        }

        $data = [
            "requestTimestamp" => $requestTimestamp,//2020-02-26 13:36:00
            "mid" => $this->merchantId,
            "qrCodeId" => $param["qrCodeId"]
        ];
        $body = json_encode($data);
        $header = [
            'Content-Type: application/json',
            'Content-Length: ' . strlen($body),
            'Authorization: ' . $this->getSign($body)
        ];

        $res = $this->curl(self::QueryStaticQrCode, 'POST', $body,
            $header);
        return $res;
    }

    public function getQrCode(array $param): array
    {
        if (!isset($param["requestTimestamp"])) {
            $requestTimestamp = date("Y-m-d H:i:s", time());
        } else {
            $requestTimestamp = $param["requestTimestamp"];
        }
        $data = [
            "requestTimestamp" => $requestTimestamp,//2020-02-26 13:36:00
            "mid" => $this->merchantId,
            "tid" => $this->terminalCode,
            "instMid" => "QRPAYDEFAULT",
            "billNo" => $param["billNo"],//"31942002267733902130410551",
            "billDate" => $param["billDate"],//date("Y-m-d", time()),//2022-02-26
            "totalAmount" => (int)$param["totalAmount"],
//            "expireTime" => "2024-08-26 13:31:59",
            "notifyUrl" => $param["notifyUrl"] ?? "",
            "returnUrl" => $param["returnUrl"] ?? "",
            "qrCodeId" => $param["qrCodeId"],//"31942002267733902130410551",
            "systemId" => $param["systemId"],//"31942002267733902130410551",
            "walletOption" => "SINGLE"
        ];
        $body = json_encode($data);
//        $token = self::Token;
//        if (empty($token)) {
//            $token = $this->getAccessToken();
//        }
//        $header=[
//            'Content-Type: application/json',
//            'Authorization:OPEN-ACCESS-TOKEN AccessToken="'.$token.'",AppId="'.self::AppId.'"'
//        ];
        $header = [
            'Content-Type: application/json',
            'Content-Length: ' . strlen($body),
            'Authorization: ' . $this->getSign($body)
        ];

        $res = $this->curl(self::GetQrCode, 'POST', $body,
            $header);
        return $res;
    }

    public function upQrcode(array $param): array
    {
        if (!isset($param["requestTimestamp"])) {
            $requestTimestamp = date("Y-m-d H:i:s", time());
        } else {
            $requestTimestamp = $param["requestTimestamp"];
        }
        $data = [
            "requestTimestamp" => $requestTimestamp,//2020-02-26 13:36:00
            "mid" => $this->merchantId,
            "tid" => $this->terminalCode,
            "instMid" => "QRPAYDEFAULT",
            "billNo" => $param["billNo"],//"31942002267733902130410551",
            "billDate" => $param["billDate"],//date("Y-m-d", time()),//2022-02-26
            "totalAmount" => (int)$param["totalAmount"],
//            "expireTime" => "2024-08-26 13:31:59",
            "notifyUrl" => $param["notifyUrl"] ?? "",
            "returnUrl" => $param["returnUrl"] ?? "",
            "qrCodeId" => $param["qrCodeId"],//"31942002267733902130410551",
            "systemId" => $param["systemId"],//"31942002267733902130410551",
            "walletOption" => "SINGLE"
        ];
        $body = json_encode($data);
//        $token = self::Token;
//        if (empty($token)) {
//            $token = $this->getAccessToken();
//        }
//        $header=[
//            'Content-Type: application/json',
//            'Authorization:OPEN-ACCESS-TOKEN AccessToken="'.$token.'",AppId="'.self::AppId.'"'
//        ];
        $header = [
            'Content-Type: application/json',
            'Content-Length: ' . strlen($body),
            'Authorization: ' . $this->getSign($body)
        ];

        $res = $this->curl(self::UpQrcode, 'POST', $body,
            $header);
        return $res;
    }


    public function closeQrcode(array $param): array
    {
        if (!isset($param["requestTimestamp"])) {
            $requestTimestamp = date("Y-m-d H:i:s", time());
        } else {
            $requestTimestamp = $param["requestTimestamp"];
        }
        $data = [
            "requestTimestamp" => $requestTimestamp,//2020-02-26 13:36:00
            "mid" => $this->merchantId,
            "tid" => $this->terminalCode,
            "instMid" => "QRPAYDEFAULT",
            "billNo" => $param["billNo"],//"31942002267733902130410551",
            "billDate" => $param["billDate"],// date("Y-m-d", time()),//2022-02-26
            "totalAmount" => (int)$param["totalAmount"],
//            "expireTime" => "2024-08-26 13:31:59",
            "notifyUrl" => $param["notifyUrl"] ?? "",
            "returnUrl" => $param["returnUrl"] ?? "",
            "qrCodeId" => $param["qrCodeId"],//"31942002267733902130410551",
            "systemId" => $param["systemId"],//"31942002267733902130410551",
            "walletOption" => "SINGLE"
        ];
        $body = json_encode($data);
//        $token = self::Token;
//        if (empty($token)) {
//            $token = $this->getAccessToken();
//        }
//        $header=[
//            'Content-Type: application/json',
//            'Authorization:OPEN-ACCESS-TOKEN AccessToken="'.$token.'",AppId="'.self::AppId.'"'
//        ];
        $header = [
            'Content-Type: application/json',
            'Content-Length: ' . strlen($body),
            'Authorization: ' . $this->getSign($body)
        ];

        $res = $this->curl(self::CloseQrcode, 'POST', $body,
            $header);
        return $res;
    }

    public function query(array $param): array
    {

        if (!isset($param["requestTimestamp"])) {
            $requestTimestamp = date("Y-m-d H:i:s", time());
        } else {
            $requestTimestamp = $param["requestTimestamp"];
        }

        $data = [
            "requestTimestamp" => $requestTimestamp,
            "mid" => $this->merchantId,
            "tid" => $this->terminalCode,
            "instMid" => "QRPAYDEFAULT",
            "billNo" => $param["billNo"],//"31942002269255200133896902",
            "refundOrderId" => $param["refundOrderId"] ?? "",//退款
            "authedOrderId" => $param["authedOrderId"] ?? "",//预授权完成订单号
            "billDate" => date("Y-m-d", time())
        ];
        $body = json_encode($data);
//        $token = self::Token;
//        if (empty($token)) {
//            $token = $this->getAccessToken();
//        }
//        $header=[
//            'Content-Type: application/json',
//            'Authorization:OPEN-ACCESS-TOKEN AccessToken="'.$token.'",AppId="'.self::AppId.'"'
//        ];
        $header = [
            'Content-Type: application/json',
            'Content-Length: ' . strlen($body),
            'Authorization: ' . $this->getSign($body)
        ];

        $res = $this->curl(self::Query, 'POST', $body,
            $header);
        return $res;
    }

    public function refund(array $param): array
    {
        if (!isset($param["requestTimestamp"])) {
            $requestTimestamp = date("Y-m-d H:i:s", time());
        } else {
            $requestTimestamp = $param["requestTimestamp"];
        }

        $data = [
            "requestTimestamp" => $requestTimestamp,//2020-02-26 13:36:00
            "mid" => $this->merchantId,
            "tid" => $this->terminalCode,
            "instMid" => "QRPAYDEFAULT",
            "billNo" => $param["billNo"],//"31942002267733902130410551",
            "billDate" => $param["billDate"],//date("Y-m-d", time()),//2022-02-26
            "refundOrderId" => $param["refundOrderId"]??"",//退款订单ID
            "refundAmount" => (int)$param["refundAmount"],//分
        ];
        $body = json_encode($data);
//        $token = self::Token;
//        if (empty($token)) {
//            $token = $this->getAccessToken();
//        }
//        $header=[
//            'Content-Type: application/json',
//            'Authorization:OPEN-ACCESS-TOKEN AccessToken="'.$token.'",AppId="'.self::AppId.'"'
//        ];
        $header = [
            'Content-Type: application/json',
            'Content-Length: ' . strlen($body),
            'Authorization: ' . $this->getSign($body)
        ];

        $res = $this->curl(self::Refund, 'POST', $body,
            $header);
        return $res;
    }

    //支付回调
    public function notify(array $post)
    {
        $params = array_map(function ($value) {
            return urldecode($value);
        }, $post);
        $this->console("通知内容:" . json_encode($params) . "\r\n");
        $md5Key = 'impARTxrQcfwmRijpDNCw6hPxaWCddKEpYxjaKXDhCaTCXJ6';    //通知验签密钥 生产环境请记得更换
        $sign = $this->makeSign($md5Key, $params);
        $this->console("自己计算的签名:" . $sign . "\r\n");
        $notifySign = array_key_exists('sign', $params) ? $params['sign'] : '';
        if (strcmp($sign, $notifySign) == 0) {
            //处理自己业务逻辑...
            //处理完自己业务逻辑后须返回SUCCESS
            echo "SUCCESS";
        } else {
            echo "UNSUCCESS";
        }
    }

    public function getAccessToken()
    {
        $url = self::AccessToken;
        $params = [
            "appId" => $this->appId,
            "timestamp" => date("YmdHis", time()),
            "nonce" => md5(uniqid(microtime(true), true)),
            "signMethod" => "SHA256",
        ];
        $str = $params['appId'] . $params['timestamp'] . $params['nonce'] . $this->appKey;
        $signature = bin2hex(hash('SHA256', $str, true));
        $params["signature"] = $signature;
        $result = $this->curl($url, 'POST', $params, ['Content-type: application/json']);
        return $result->accessToken ?? "";
    }

    public function getSign($body)
    {
        $timestamp = date("YmdHis", time());
        $nonce = md5(uniqid(microtime(true), true));
        $str = bin2hex(hash('sha256', $body, true));
        $signature = base64_encode(hash_hmac('sha256', $this->appId . "$timestamp$nonce$str", $this->appKey, true));
        $authorization = "OPEN-BODY-SIG AppId=\"" . $this->appId . "\", Timestamp=\"$timestamp\", Nonce=\"$nonce\", Signature=\"$signature\"";
        return $authorization;
    }

    public function console($data)
    {
        $stdout = fopen('php://stdout', 'w');
        fwrite($stdout, $data . "\n");
        fclose($stdout);
    }

    public function curl($url, $method, $params, $header = []): array
    {
        //初始化CURL句柄
        $curl = curl_init();

        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
        if (!empty($header)) {
            curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
        }
        //请求时间
        $timeout = 30;
        curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, $timeout);
        switch ($method) {
            case "GET" :
                curl_setopt($curl, CURLOPT_HTTPGET, true);
                break;
            case "POST":
                if (is_array($params)) {
                    $params = json_encode($params, 320);
                }
                curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "POST");
                curl_setopt($curl, CURLOPT_POSTFIELDS, $params);
                break;
        }
        $data = curl_exec($curl);
        $status = curl_getinfo($curl, CURLINFO_HTTP_CODE);
        curl_close($curl);//关闭cURL会话

        return json_decode($data,true);
    }

    function makeSign($md5Key, $params): string
    {
        $str = $this->buildSignStr($params) . $md5Key;
        //file_put_contents('log.txt', "待验签字符串:".$str."\r\n", FILE_APPEND);
        if ($params['signType'] == 'SHA256') {
            return strtoupper(hash('sha256', $str));
        }
        return strtoupper(hash('md5', $str));
    }


    //获取加密的参数字符串
    function buildSignStr($params): string
    {
        $keys = [];
        foreach ($params as $key => $value) {
            if ($key == 'sign' || is_null($value)) {
                continue;
            }
            array_push($keys, $key);
        }
        $str = '';
        sort($keys);
        $len = count($keys);
        for ($i = 0; $i < $len; $i++) {
            $v = $params[$keys[$i]];
            if (is_array($v)) {
                $v = json_encode($v);
            }
            $str .= $keys[$i] . '=' . $v . (($i === $len - 1) ? '' : "&");
        }
        return $str;
    }

}

